<script setup lang="ts">
import { nextTick, ref, useSlots, useTemplateRef, watch } from 'vue';
import type { PopoverPosition } from './enum';

const props = withDefaults(defineProps<{ placement: PopoverPosition }>(), {
  placement: 'BOTTOM_LEFT'
});

const slots = useSlots();

const isVisible = ref<boolean>(false);

const referenceRef = useTemplateRef<HTMLDivElement>('referenceRef');
const popoverRef = useTemplateRef<HTMLDivElement>('popoverRef');
const getElementSize = (target: HTMLElement | null) => {
  if (!target) return null;

  return { width: target.offsetWidth, height: target.offsetHeight };
};

const popoverStyle = ref<{ top: number | string; left: number | string }>({
  top: 0,
  left: 0
});
watch(isVisible, val => {
  if (!val) {
    return;
  }

  nextTick(() => {
    switch (props.placement) {
      case 'TOP_LEFT':
        popoverStyle.value.top = 0;
        popoverStyle.value.left =
          popoverRef && getElementSize(popoverRef.value)
            ? -getElementSize(popoverRef.value)!.width + 'px'
            : '0px';
        break;
      case 'TOP_RIGHT':
        popoverStyle.value.top = 0;
        popoverStyle.value.left =
          referenceRef && getElementSize(referenceRef.value)
            ? getElementSize(referenceRef.value)!.width + 'px'
            : '0px';
        break;
      case 'BOTTOM_LEFT':
        popoverStyle.value.top =
          referenceRef && getElementSize(referenceRef.value)
            ? getElementSize(referenceRef.value)!.height + 'px'
            : '0px';
        popoverStyle.value.left =
          popoverRef && getElementSize(popoverRef.value)
            ? -getElementSize(popoverRef.value)!.width + 'px'
            : '0px';
        break;
      case 'BOTTOM_RIGHT':
        popoverStyle.value.top =
          referenceRef && getElementSize(referenceRef.value)
            ? getElementSize(referenceRef.value)!.height + 'px'
            : '0px';
        popoverStyle.value.left =
          referenceRef && getElementSize(referenceRef.value)
            ? getElementSize(referenceRef.value)!.width + 'px'
            : '0px';

        break;
      default:
        break;
    }
  });
});

let timer: number | null = null;
const onMouseEnter = () => {
  if (slots.default) {
    isVisible.value = true;
  }

  if (timer) {
    clearTimeout(timer);
  }
};
const onMouseLeave = () => {
  timer = setTimeout(() => {
    isVisible.value = false;
    timer = null;
  }, 100);
};

defineOptions({ name: 'MPopover' });
</script>

<template>
  <div class="relative" @mouseleave="onMouseLeave" @mouseenter="onMouseEnter">
    <div ref="referenceRef">
      <!-- 具名插槽:触发弹出层 -->
      <slot name="reference" />
    </div>
    <Transition name="popup">
      <div
        ref="popoverRef"
        v-if="isVisible"
        :style="popoverStyle"
        class="absolute z-20 rounded-md border border-zinc-300 bg-white p-1 dark:border-zinc-700 dark:bg-zinc-900"
      >
        <!-- 匿名插槽:弹出层视图 -->
        <slot />
      </div>
    </Transition>
  </div>
</template>

<style lang="css" scoped>
.popup-enter-active,
.popup-leave-active {
  transition: opacity 0.3s transform 0.3s;
}

.popup-enter-from,
.popup-leave-to {
  opacity: 0;
  transform: translateY(20px);
}
</style>
